package com.pluszg.nutrition.common.utils;

import com.pluszg.nutrition.common.bean.ApplicationConstants;
import com.pluszg.nutrition.common.bean.EnumDatePattern;
import com.pluszg.nutrition.common.bean.EnumTimeUnit;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;

import static lombok.AccessLevel.PRIVATE;

/**
 * 时间工具类
 */
@NoArgsConstructor(access = PRIVATE)
public class DateUtil {

    /**
     * 获取系统默认的日期格式化对象 pattern为yyyy-MM-dd HH:mm:ss
     *
     * @return
     */
    public static FastDateFormat getDefaultFormatter() {
        return getFormatter(EnumDatePattern.DEFAULT_PATTERN);
    }

    /**
     * 获取指定格式的日期格式化对象
     *
     * @return
     */
    public static FastDateFormat getFormatter(EnumDatePattern datePattern) {
        if (datePattern == null) {
            datePattern = EnumDatePattern.DEFAULT_PATTERN;
        }
        return getFormatter(datePattern.desc());
    }

    /**
     * 获取指定格式的日期格式化对象
     *
     * @return
     */
    public static FastDateFormat getFormatter(String pattern) {
        if (StringUtils.isBlank(pattern)) {
            pattern = EnumDatePattern.DEFAULT_PATTERN.desc();
        }
        return FastDateFormat.getInstance(pattern, ApplicationConstants.APP_TIMEZONE);
    }

    /**
     * 将指定日期转成默认格式yyyy-MM-dd HH:mm:ss的字符串
     *
     * @param date
     * @return
     */
    public static String format(Date date) {
        if (date == null) {
            return null;
        }
        return getDefaultFormatter().format(date);
    }

    /**
     * 将指定日期转成指定格式的字符串
     *
     * @param date
     * @return
     */
    public static String format(Date date, EnumDatePattern datePattern) {
        if (ObjectUtils.allNotNull(date, datePattern)) {
            return format(date, datePattern.desc());
        }
        return format(date);
    }

    /**
     * 将指定日期转换为指定格式的字符串
     *
     * @param date
     * @param pattern
     * @return
     */
    public static String format(Date date, String pattern) {
        if (date != null && StringUtils.isNotBlank(pattern)) {
            FastDateFormat formatter = getFormatter(pattern);
            if (formatter != null) {
                return formatter.format(date);
            }
        }
        return format(date);
    }

    /**
     * 日期字符串转成默认格式yyyy-MM-dd HH:mm:ss的日期
     *
     * @param dateStr
     * @return
     */
    public static Date parse(String dateStr) {
        if (StringUtils.isBlank(dateStr)) {
            return null;
        }
        try {
            return getDefaultFormatter().parse(dateStr);
        } catch (ParseException e) {
            throw new RuntimeException("日期格式错误");
        }
    }

    /**
     * 日期字符串转指定格式的日期
     *
     * @param dateStr
     * @return
     */
    public static Date parse(String dateStr, EnumDatePattern datePattern) {
        if (datePattern != null && StringUtils.isNotBlank(dateStr)) {
            return parse(dateStr, datePattern.desc());
        }
        return parse(dateStr);
    }

    /**
     * 将指定的日期字符串格式转成指定格式的日期
     *
     * @param dateStr
     * @param pattern
     * @return
     */
    public static Date parse(String dateStr, String pattern) {
        if (StringUtils.isAnyBlank(dateStr, pattern)) {
            return parse(dateStr);
        }
        FastDateFormat formatter = getFormatter(pattern);
        try {
            if (formatter != null) {
                return formatter.parse(dateStr);
            }
            return parse(dateStr);
        } catch (ParseException e) {
            throw new RuntimeException("日期格式错误");
        }
    }

    public static Date tryParse(String dateStr) {
        for (EnumDatePattern datePattern : EnumDatePattern.values()) {
            try {
                return parse(dateStr, datePattern);
            } catch (Exception e) {
                //ignore exception
            }
        }
        throw new RuntimeException("Not supported date pattern");
    }

    /**
     * 时间戳转日期
     *
     * @param timeStamp
     * @return
     */
    public static Date timeStamp2Date(Long timeStamp) {
        if (timeStamp != null) {
            return new Date(timeStamp * 1000);
        }
        return null;
    }

    /**
     * 日期转时间戳
     *
     * @param date
     * @return
     */
    public static Integer date2TimeStamp(Date date) {
        if (date != null) {
            return new Long(date.getTime() / 1000).intValue();
        }
        return null;
    }

    /**
     * 获取当前时间
     *
     * @return
     */
    public static Date getCurrentDate() {
        return new Date();
    }

    /**
     * 获取format后的当前时间
     *
     * @return
     */
    public static String getCurrentFormatDate(EnumDatePattern pattern) {
        return format(getCurrentDate(), pattern);
    }

    /**
     * 获取当前时间戳
     *
     * @return
     */
    public static long getTimeStamp() {
        return getCurrentDate().getTime();
    }


    /**
     * 获取日期中的年份
     */
    public static int getYear(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c.get(Calendar.YEAR);
    }

    /**
     * 获取日期中的月份
     */
    public static int getMonth(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c.get(Calendar.MONTH) + 1;
    }

    /**
     * 获取日期中的天
     */
    public static int getDay(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c.get(Calendar.DATE);
    }

    /**
     * 指定日期、日期间隔、间隔单位，返回间隔之前 | 之后的日期
     *
     * @param date         指定日期
     * @param interval     日期间隔 为正数时返回间隔之后的日期，为负数时返回间隔之前的日期
     * @param intervalUnit 间隔单位
     * @return
     */
    public static Date getSpecifiedDay(Date date, int interval, EnumTimeUnit intervalUnit) {
        Assert.isTrue(ObjectUtils.allNotNull(date, intervalUnit), "时间与时间间隔均不能为空");

        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.setTime(date);
        switch (intervalUnit) {
            case YEAR:
                calendar.add(Calendar.YEAR, interval);
                break;
            case MONTH:
                calendar.add(Calendar.MONTH, interval);
                break;
            case WEEK:
                calendar.add(Calendar.DATE, 7 * interval);
                break;
            case DAY:
                calendar.add(Calendar.DATE, interval);
                break;
            case HOUR:
                calendar.add(Calendar.HOUR, interval);
                break;
            case MINUTE:
                calendar.add(Calendar.MINUTE, interval);
                break;
            case SECOND:
                calendar.add(Calendar.SECOND, interval);
                break;
            default:
                throw new RuntimeException("不支持的日期间隔");
        }
        return calendar.getTime();
    }

    /**
     * 获取两个时间点之间的所有日期
     *
     * @param startDate
     * @param endDate
     * @param datePattern
     * @return
     */
    public static Set<String> getDistanceDays(Date startDate, Date endDate, EnumDatePattern datePattern) {
        Set<String> days = new LinkedHashSet<>();
        try {
            if (!ObjectUtils.allNotNull(startDate, endDate, datePattern)) {
                throw new NullPointerException();
            }
            Calendar tempStart = Calendar.getInstance();
            tempStart.setTime(startDate);

            Calendar tempEnd = Calendar.getInstance();
            tempEnd.setTime(endDate);
            tempEnd.add(Calendar.DATE, 1);
            while (tempStart.before(tempEnd)) {
                days.add(format(tempStart.getTime(), datePattern));
                tempStart.add(Calendar.DAY_OF_YEAR, 1);
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
        return days;
    }

    /**
     * 获取一天的开始时间，即 设置参数date的时分秒为0
     *
     * @param date
     * @return
     */
    public static Date getStartDayTime(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 000000);
        return c.getTime();
    }

    /**
     * 获取一天的开始时间，即 设置参数date的时分秒为23:59:59
     *
     * @param date
     * @return
     */
    public static Date getEndDayTime(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999999);
        return c.getTime();
    }

    /**
     * 获取一周的开始时间
     *
     * @param date
     * @return
     */
    public static Date getStartWeekTime(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        return c.getTime();
    }

    /**
     * 获取一周的结束时间
     *
     * @param date
     * @return
     */
    public static Date getEndWeekTime(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        return c.getTime();
    }

    /**
     * 获取一月的开始时间
     *
     * @param date
     * @return
     */
    public static Date getStartMonthTime(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        return c.getTime();
    }

    /**
     * 获取一月的结束时间
     *
     * @param date
     * @return
     */
    public static Date getEndMonthTime(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.MONTH, 1);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.add(Calendar.DATE, -1);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        return c.getTime();
    }

    /**
     * 获取两个日期之间的间隔天数
     *
     * @param startDate 较小的时间
     * @param endDate   较大的时间
     * @return 相差天数
     */
    public static int getBetweenDay(Date startDate, Date endDate) {
        Date newStartDate = getStartDayTime(startDate);
        Date newEndDate = getStartDayTime(endDate);

        Calendar cal = Calendar.getInstance();
        cal.setTime(newStartDate);
        long time1 = cal.getTimeInMillis();
        cal.setTime(newEndDate);
        long time2 = cal.getTimeInMillis();

        Long days = (time2 - time1) / (1000 * 3600 * 24);
        return days.intValue();
    }

    /**
     * 获取两个字符串格式的日期之间的间隔天数
     *
     * @param startDateStr 较小的时间
     * @param endDateStr   较大的时间
     * @return 相差天数
     */
    public static int getBetweenDay(String startDateStr, String endDateStr) {
        return getBetweenDay(tryParse(startDateStr), tryParse(endDateStr));
    }

    /**
     * LocalDate转Date
     *
     * @param localDate
     * @return
     */
    public static Date localDate2Date(LocalDate localDate) {
        return Optional.ofNullable(localDate)
                .map(ld -> Date.from(ld.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()))
                .orElseThrow(() -> new NullPointerException("localDate must be not null!"));
    }

    /**
     * Date转LocalDate
     *
     * @param date
     * @return
     */
    public static LocalDate date2LocalDate(Date date) {
        return Optional.ofNullable(date)
                .map(d -> d.toInstant().atZone(ZoneId.systemDefault()).toLocalDate())
                .orElseThrow(() -> new NullPointerException("date must be not null!"));
    }

    /**
     * 根据生日计算年龄
     *
     * @param birthDay
     * @return
     */
    public static int calAge(Date birthDay) {
        Calendar cal = Calendar.getInstance();
        if (cal.before(birthDay)) {
            return 0;
        }
        int yearNow = cal.get(Calendar.YEAR);
        int monthNow = cal.get(Calendar.MONTH);
        int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH);
        cal.setTime(birthDay);

        int yearBirth = cal.get(Calendar.YEAR);
        int monthBirth = cal.get(Calendar.MONTH);
        int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
        int age = yearNow - yearBirth;
        if (monthNow <= monthBirth) {
            if (monthNow == monthBirth) {
                if (dayOfMonthNow < dayOfMonthBirth)
                    age--;
            } else {
                age--;
            }
        }
        return age;
    }

    /**
     * 根据年龄反算生日，日期取1月1日
     *
     * @param age
     * @return
     */
    public static Date calBirthDay(int age) {
        Calendar cal = Calendar.getInstance();
        int birthYear = cal.get(Calendar.YEAR) - age;
        cal.set(Calendar.YEAR, birthYear);
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        return cal.getTime();
    }

    /**
     * 获取时间月份 yyyyMM
     *
     * @param date
     * @return
     */
    public static int getDateMonthValue(Date date) {
        return Optional.ofNullable(date)
                .map(d -> Integer.parseInt(format(date, EnumDatePattern.PATTERN_13)))
                .orElseThrow(() -> new NullPointerException("date must be not null!"));
    }


    /**
     * 获取两个日期之间的间隔分钟
     *
     * @param startTime 较小的时间
     * @param endTime   较大的时间
     * @return 相差分钟
     */
    public static int getTimeDifference(Date startTime, Date endTime) {

        long time1 = startTime.getTime();

        long time2 = endTime.getTime();

        Long minute = (time2 - time1) / (1000 * 60);
        return minute.intValue();
    }
}
